#include "modbus_ui.h"
#include "ui_modbusui.h"
#include <QDebug>
#include <QMessageBox>
#include <QDateTime>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include <QModbusDataUnit>
#include <QModbusTcpClient>
#include <QModbusRtuSerialMaster>

#include <iostream>

ModbusUI::ModbusUI(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::ModbusUI)
{
    qRegisterMetaType<QVariant>("QVariant");
    std::cout << "children thread:" << std::this_thread::get_id()<< std::endl;
    ui->setupUi(this);

    //1-ui add
    sbhex_writeAddr = new HexSpinBox(false);
//    sbhex_writeAddr->setMinimum(QString("0x63").toInt(nullptr, 16));
//    sbhex_writeAddr->setMinimum(_MODBUS_START_ADDR_);
    sbhex_writeData = new HexSpinBox(false);
    ui->gridLayout_4->addWidget(sbhex_writeAddr,0,1);
    ui->gridLayout_4->addWidget(sbhex_writeData,1,1);

    //2-modbus
    m_modbusInterface = new ModbusInterface();
    on_tabWidget_currentChanged(ui->tabWidget->currentIndex());
    connect(this, &ModbusUI::SN_modbusRead, m_modbusInterface, &ModbusInterface::SL_modbusRead);
    connect(this, &ModbusUI::SN_modbusWrite, m_modbusInterface, &ModbusInterface::SL_modbusWrite);
    connect(this, &ModbusUI::SN_modbusWriteArray, m_modbusInterface, &ModbusInterface::SL_modbusWriteArray);

    connect(m_modbusInterface, &ModbusInterface::SN_queuePop, this, [this](){
        ui->pte_list->clear();
        foreach(struct _modbusWorkStruct t_mwss, m_modbusInterface->m_queueModbusWork)
            if(t_mwss.readOrNot)
                ui->pte_list->appendPlainText("read\t"+QString::number(t_mwss.addr,16)+":"+QString::number(t_mwss.amount,16)+"-"+QString::number(t_mwss.delayTime));
            else{
                QString t_str("write\t"+QString::number(t_mwss.addr,16)+":");
                foreach(quint16 t, t_mwss.vec)
                    t_str += "0x"+QString::number(t,16)+" ";
                t_str += "-"+QString::number(t_mwss.delayTime);
                ui->pte_list->appendPlainText(t_str);
            }
    });
    connect(m_modbusInterface, &ModbusInterface::SN_queuePush, this, [this](){
        struct _modbusWorkStruct t_mwss=m_modbusInterface->m_queueModbusWork.back();
        if(t_mwss.readOrNot)
            ui->pte_list->appendPlainText("read\t"+QString::number(t_mwss.addr,16)+":"+QString::number(t_mwss.amount,16)+"-"+QString::number(t_mwss.delayTime));
        else{
            QString t_str("write\t"+QString::number(t_mwss.addr,16)+":");
            foreach(quint16 t, t_mwss.vec)
                t_str += "0x"+QString::number(t,16)+" ";
            t_str += "-"+QString::number(t_mwss.delayTime);
            ui->pte_list->appendPlainText(t_str);
        }
    });
    connect(m_modbusInterface, &ModbusInterface::SN_readFinsih, this, [this](QByteArray byteArraySendData){
        if(m_clientConnection!=nullptr)
            if(m_clientConnection->isOpen() && m_clientConnection->isWritable())
            {
//                ui->pte_readText->appendPlainText(QString::number(byteArraySendData.size()));
                m_clientConnection->write(byteArraySendData);
            }
    });
    connect(m_modbusInterface, &ModbusInterface::SN_printMsg, this, [this](int index, QString strMsg){
        if(ui->cb_displaymsg->isChecked())
        switch (index)
        {
        case 1:
            ui->pte_readText->appendPlainText(strMsg);
            break;
        case 2:
            ui->pte_readText2->appendPlainText(strMsg);
            break;
        case 3:
            statusBar()->showMessage(strMsg, 5000);
            break;
        default:
            break;
        }
    });

    //3-error select
    m_delayTimer1 = new QTimer(this);
    m_delayTimer2 = new QTimer(this);
    connect(m_modbusInterface, &ModbusInterface::SN_modbusError, this, [this](int index){
        if(index==QModbusDevice::TimeoutError||index==QModbusDevice::ConnectionError)
        {
            stopPeriodTimer();
        }

        QDateTime current_date_time =QDateTime::currentDateTime();
        QString current_time =current_date_time.toString("yyyy-MM-dd hh:mm:ss");
        ui->pte_readText->appendPlainText("modbus connect crash: " + current_time + "id" + index);

        bool t_comFlag=true;
        if(ui->btn_connect_tcp->text().contains("disconnect"))
            t_comFlag = false;

        if(index==QModbusDevice::TimeoutError||index==QModbusDevice::ConnectionError)
            m_delayTimer1->singleShot(ui->sb_combreaktime->value(), this, [this](){
                m_modbusInterface->modbusDevice->disconnectDevice();
                ui->btn_connect_com->setText(tr("connect"));
                ui->btn_connect_tcp->setText(tr("connect"));
            });

        if(index==QModbusDevice::TimeoutError)
            m_delayTimer2->singleShot(ui->sb_combreaktime->value()+ui->sb_comrectime->value(), this, [this, t_comFlag](){
                if(t_comFlag)
                    on_btn_connect_com_clicked();
                else
                    on_btn_connect_tcp_clicked();
                m_delayTimer1->singleShot(ui->sb_modserverrectime->value(), this, [this](){
                    startPeriodTimer();
                });
            });
    });


    //4-scan
//    on_btn_scan_clicked();
//    on_btn_connect_com_clicked();
    m_delayTimer1->singleShot(2000, this, [this](){
        on_btn_connect_tcp_clicked();
        m_delayTimer1->singleShot(2000, this, [this](){
            startPeriodTimer();
        });
    });

    //5-init period time
    m_readPeriodTimer= new QTimer(this);
    connect(m_readPeriodTimer, SIGNAL(timeout()), this, SLOT(on_btn_readGroup_clicked()));

    //6-local socket
    m_server = new QLocalServer(this);
    m_server->setMaxPendingConnections(1);
    on_btn_socketConnect_clicked();
    m_clientConnection = nullptr;
    connect(m_server, &QLocalServer::newConnection, this, [this](){
        //m_clientConnection already connect, close new connection
        if(m_clientConnection!=nullptr)
            if(m_clientConnection->isOpen())
            {
                qDebug()<<"already connect";
                QLocalSocket *clientConnection = m_server->nextPendingConnection();
                clientConnection->disconnectFromServer();
                clientConnection->close();
                free(clientConnection);
                return;
            }
            else
                m_clientConnection = m_server->nextPendingConnection();
        else
            m_clientConnection = m_server->nextPendingConnection();

        connect(m_clientConnection, &QLocalSocket::disconnected,
                m_clientConnection, [this](){
            m_clientConnection->disconnectFromServer();
            m_clientConnection->close();
            free(m_clientConnection);
            m_clientConnection = nullptr;
        });


        connect(m_clientConnection, &QLocalSocket::readyRead, this, [this](){
            QByteArray t_byar;
            qDebug()<< t_byar;
            t_byar = m_clientConnection->readAll();
            qDebug() << t_byar;
            for(int pointer=0; pointer<t_byar.length(); )
            {
                if(t_byar.at(pointer)==0x00)
                {
                    SN_modbusWrite((unsigned char)t_byar.at(1+pointer)|(unsigned char)t_byar.at(2+pointer)<<8, 0x01, ui->sb_delay->value());
                    SN_modbusWrite((unsigned char)t_byar.at(1+pointer)|(unsigned char)t_byar.at(2+pointer)<<8, 0x00, ui->sb_delay->value());
                    pointer+=5;
                }
                else if(t_byar.at(pointer)==0x01)
                {
                    SN_modbusWrite((unsigned char)t_byar.at(1+pointer)|(unsigned char)t_byar.at(2+pointer)<<8,
                                   (unsigned char)t_byar.at(3+pointer)|(unsigned char)t_byar.at(4+pointer)<<8,
                                   ui->sb_delay->value());
                    pointer+=5;
                }
                else if(t_byar.at(pointer)>=0x02)
                {
                    QVector<quint16> t_vec;
                    for(int i=0+pointer; i<t_byar.at(0+pointer); i++)
                        t_vec.push_back((unsigned char)t_byar.at(3+i*2)|(unsigned char)t_byar.at(4+i*2)<<8);
                    SN_modbusWriteArray((unsigned char)t_byar.at(1+pointer)|(unsigned char)t_byar.at(2+pointer)<<8,
                                        t_vec, ui->sb_delay->value());
                    pointer+=(3+t_vec.size()*2);
                }
                else{
                    qDebug("rec undefine data");
                }

            }
        });

    });
}

ModbusUI::~ModbusUI()
{
    if(m_server->isListening())
    {
        m_server->close();
        ui->btn_socketConnect->setText("socket connect");
        ui->le_socketConnect->setEnabled(true);
    }
    delete ui;
}

/*************** modbus phy interface ****************/
void ModbusUI::on_btn_scan_clicked()
{
    ui->cbb_comSelect->clear();
//    ui->cbb_comSelect->addItem("COM24");
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        QSerialPort serial;
        serial.setPort(info);
        /* 判断端口是否能打开 */
        if(serial.open(QIODevice::ReadWrite))
        {
            bool isHaveItemInList = true;
            //判断是不是已经在列表中了
            for(int i=0; i<ui->cbb_comSelect->count(); i++)
                // 如果，已经在列表中了，那么不添加这一项了就
                if(ui->cbb_comSelect->itemData(i) == serial.portName())
                    isHaveItemInList=false;

            if(isHaveItemInList)
                ui->cbb_comSelect->addItem(serial.portName());
            serial.close();
        }
    }
}

void ModbusUI::on_btn_connect_com_clicked()
{
//    if (!m_modbusInterface->modbusDevice){
//        m_modbusInterface->modbusDevice = new QModbusRtuSerialMaster();
//        qDebug() << "no device";
//        return;
//    }
    if (m_modbusInterface->modbusDevice->state() != QModbusDevice::ConnectedState) {
        m_modbusInterface->modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
                                                                ui->cbb_comSelect->currentText());
        m_modbusInterface->modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
                                                                QSerialPort::EvenParity);
        m_modbusInterface->modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
                                                                QSerialPort::Baud19200);
        m_modbusInterface->modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
                                                                QSerialPort::Data8);
        m_modbusInterface->modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
                                                                QSerialPort::OneStop);

        m_modbusInterface->modbusDevice->setTimeout(900);
        m_modbusInterface->modbusDevice->setNumberOfRetries(1);
        if (!m_modbusInterface->modbusDevice->connectDevice()) {
            m_modbusInterface->SN_printMsg(3, tr("Connect failed: ") + m_modbusInterface->modbusDevice->errorString());
        }
        else {
            ui->btn_connect_com->setText(tr("disconnect"));
        }
    }
    else {
        m_modbusInterface->modbusDevice->disconnectDevice();
        ui->btn_connect_com->setText(tr("connect"));
    }
}

void ModbusUI::on_btn_connect_tcp_clicked()
{
    //    SN_modbusConnect(ui->cbb_comSelect->currentText());
//        if (!m_modbusInterface->modbusDevice){
//            m_modbusInterface->modbusDevice = new QModbusTcpClient(this);
//            connect(m_modbusInterface->modbusDevice, &QModbusClient::stateChanged,
//                    this, [this](int state){
//                if(state == QModbusDevice::UnconnectedState)
//                    ui->btn_connect_tcp->setText(tr("connect"));
//                else if (state == QModbusDevice::ConnectedState)
//                    ui->btn_connect_tcp->setText(tr("disconnect"));
//                qDebug() << "modbus statu change";
//            });
//            return;
//        }
    if (m_modbusInterface->modbusDevice->state() != QModbusDevice::ConnectedState) {
        const QUrl url = QUrl::fromUserInput(ui->le_tcpUrl->text());
        m_modbusInterface->modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
        m_modbusInterface->modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
        m_modbusInterface->modbusDevice->setTimeout(900);
        m_modbusInterface->modbusDevice->setNumberOfRetries(1);
        m_modbusInterface->modbusDevice->connectDevice();
        qDebug() << "tcp connect";
    }
    else {
        m_modbusInterface->modbusDevice->disconnectDevice();
        ui->btn_connect_tcp->setText(tr("connect"));
    }
}

void ModbusUI::on_tabWidget_currentChanged(int index)
{
    if (m_modbusInterface->modbusDevice)
    {
        if(m_modbusInterface->modbusDevice->state() == QModbusDevice::ConnectedState)
            m_modbusInterface->modbusDevice->disconnectDevice();
        delete m_modbusInterface->modbusDevice;
    }
    if(index==0)
    {
        m_modbusInterface->modbusDevice = new QModbusRtuSerialMaster(this);
        connect(m_modbusInterface->modbusDevice, &QModbusClient::stateChanged,
                this, [this](int state){
            if(state == QModbusDevice::UnconnectedState)
            {
                stopPeriodTimer();
                ui->btn_connect_com->setText(tr("connect"));
                ui->tabWidget->setTabEnabled(1,true);
            }
            else if (state == QModbusDevice::ConnectedState)
            {
                ui->btn_connect_com->setText(tr("disconnect"));
                ui->tabWidget->setTabEnabled(1,false);
            }
//            qDebug() << "modbus statu change:rtu";
        });
    }
    else
    {
        m_modbusInterface->modbusDevice = new QModbusTcpClient(this);
        connect(m_modbusInterface->modbusDevice, &QModbusClient::stateChanged,
                this, [this](int state){
            if(state == QModbusDevice::UnconnectedState)
            {
                stopPeriodTimer();
                ui->btn_connect_tcp->setText(tr("connect"));
                ui->tabWidget->setTabEnabled(0,true);
            }
            else if (state == QModbusDevice::ConnectedState)
            {
                ui->btn_connect_tcp->setText(tr("disconnect"));
                ui->tabWidget->setTabEnabled(0,false);
            }
//            qDebug() << "modbus statu change:tcp";
        });
    }
//    qDebug() << index;
    connect(m_modbusInterface->modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
        statusBar()->showMessage(m_modbusInterface->modbusDevice->errorString(), 5000);
    });
}

/*************** ui server connect ****************/
void ModbusUI::startPeriodTimer()
{
    m_readPeriodTimer->start(ui->sb_modserverperiod->value());
    ui->btn_startTimer->setText(tr("停止modbus自动读取服务"));
    ui->btn_startTimer->setChecked(true);
    ui->sb_modserverperiod->setEnabled(false);
}

void ModbusUI::stopPeriodTimer()
{
    m_readPeriodTimer->stop();
    ui->btn_startTimer->setText(tr("开始modbus自动读取服务"));
    ui->btn_startTimer->setChecked(false);
    ui->sb_modserverperiod->setEnabled(true);
}

void ModbusUI::on_btn_startTimer_clicked()
{
    if(m_readPeriodTimer->isActive())
    {
        stopPeriodTimer();
    }
    else
    {
        startPeriodTimer();
    }
}

/*************** modbus setting ****************/
void ModbusUI::on_sb_stationNum_valueChanged(int arg1)
{
    m_modbusInterface->m_stationNum = arg1;
}

/*************** test ****************/
void ModbusUI::on_btn_impulse_clicked()
{
    SN_modbusWrite(sbhex_writeAddr->hexValue(), 0x01, ui->sb_delay->value());
    SN_modbusWrite(sbhex_writeAddr->hexValue(), 0x00, ui->sb_delay->value());
}

void ModbusUI::on_btn_writetest_clicked()
{
    SN_modbusWrite(sbhex_writeAddr->hexValue(), sbhex_writeData->hexValue(), ui->sb_delay->value());
}

void ModbusUI::on_btn_readGroup_clicked()
{
    static int s_counter=0;

    SN_modbusRead(0x63, 0x82-0x62, ui->sb_delay->value());
    SN_modbusRead(0x93, 0x30, ui->sb_delay->value());

    switch (s_counter) {
    case 0:
        ModbusReadReg(0x4101, 0x411D-0x4101+1, ui->sb_delay->value());
        break;
    case 1:
        ModbusReadReg(0x4149, 0x4169-0x4149+1, ui->sb_delay->value());
        break;
    case 2:
        ModbusReadReg(0x4178, 0x41AD-0x4178+1, ui->sb_delay->value());//54
        break;
    case 3:
        ModbusReadReg(0x41B7, 0x41EC-0x41B7+1, ui->sb_delay->value());//54
        break;
    case 4:
        ModbusReadReg(0x48f0, 6, ui->sb_delay->value());    //对时
        break;
    case 5:
        ModbusReadReg(0x5101, 0x511D-0x5101+1, ui->sb_delay->value());
        break;
    case 6:
        ModbusReadReg(0x5149, 0x5169-0x5149+1, ui->sb_delay->value());
        break;
    case 7:
        ModbusReadReg(0x5178, 0x51AD-0x5178+1, ui->sb_delay->value());
        break;
    case 8:
        ModbusReadReg(0x51B7, 0x51EC-0x51B7+1, ui->sb_delay->value());
        s_counter=0;
        return;
    default:
        break;
    }
    s_counter++;
}

void ModbusUI::on_btn_readtest_clicked()
{
    unsigned int now = sbhex_writeAddr->hexValue();
    unsigned int end = sbhex_writeAddr->hexValue()+ui->sb_readSize->value();
    for(; now+_MODBUS_READ_STEP<end; now+=_MODBUS_READ_STEP)
        SN_modbusRead(now, _MODBUS_READ_STEP, ui->sb_delay->value());
    SN_modbusRead(now, end-now, ui->sb_delay->value());
}

//void ModbusUI::on_btn_test2_clicked()
//{
//    SN_modbusWrite(0xE3, 0x01, ui->sb_delay->value());
//    SN_modbusWrite(0xE3, 0x00, ui->sb_delay->value());
//}

/*************** developer ****************/
void ModbusUI::on_btn_socketConnect_clicked()
{
    if(m_server->isListening())
    {
        m_server->close();
        ui->btn_socketConnect->setText("socket connect");
        ui->le_socketConnect->setEnabled(true);
    }
    else
        if (!m_server->listen(ui->le_socketConnect->text())) {
            QMessageBox::critical(this, tr("Local Fortune Server"),
                                  tr("Unable to start the server: %1.")
                                  .arg(m_server->errorString()));
        }
        else {
            ui->btn_socketConnect->setText("socket disconnect");
            ui->le_socketConnect->setEnabled(false);
        }
}

void ModbusUI::on_cb_displaymsg_stateChanged(int arg1)
{
//    if(arg1==Qt::Checked){
//        ui->pte_readText->setVisible(true);
//        ui->pte_readText2->setVisible(true);
//    }
//    else{
//        ui->pte_readText->setVisible(false);
//        ui->pte_readText2->setVisible(false);
//        this->adjustSize();
//    }
}
